home *** CD-ROM | disk | FTP | other *** search
- /*
- MiscSerialPort.m -- an OO wrapper around the serial ports
- Written by Matt Brandt Copyright ( c ) 1994 by Matt Brandt.
- Modified by Arnaud Meuret
- Version 2.0. All rights reserved.
- This notice may not be removed from this source code.
-
- This object is included in the MiscKit by permission from the author
- and its use is governed by the MiscKit license, found in the file
- "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
- for a list of all applicable permissions and restrictions.
- ______________________________________________________________________________*/
-
- #import "MiscSerialPort.h"
-
- struct
- {
- int intValue;
- int name;
- } baudRateTable[] = {
- {110,B110},
- {300,B300},
- {600,B600},
- {1200,B1200},
- {2400,B2400},
- {4800,B4800},
- {9600,B9600},
- {14400,B14400},
- {19200,B19200},
- {28800,B28800},
- {38400,B38400},
- {43200,B43200},
- {57600,B57600},
- {"",0}};
-
- int origLineDisc;
- struct sgttyb origTtyParms;
- struct tchars origSpecChars;
- struct ltchars origLocSpecChars;
- int origLocMode;
-
- @implementation MiscSerialPort
-
- //========================================================================
- - init
- {
- ttyParms.sg_flags = 0; // clear all delay selection
- ioctl( portFD,TIOCEXCL,&ttyParms ); // exclusive access
- ioctl( portFD,TIOCHPCL,&ttyParms ); // hang up on close
-
- // These set the default behaviour of the instances
- [self setDeviceName:"/dev/cufa"]; // port A (??name?? on white) with hard flow control
- flags.connected = NO; // not connected
- flags.suspended = NO; // not suspended
- delegate = nil; // no default delegate
- [self setBaud:B9600]; // 9600 bauds
- [self setParity:MISC_SP_NONE]; // no parity
- [self setMode:MISC_SP_COOKED]; // full I/O processing
- [self translateNL:YES]; // translate NewLines
- [self setEchoing:NO]; // do not echo received chars
- [self setFlowControl:MISC_SP_HARDFC]; // assume hardware flow control
- return self;
- }
-
- //========================================================================
- - free
- {
- [self disconnect];
- return [super free]; // so long and thanks for all the fish.
- }
-
- //========================================================================
- - ( BOOL )connect
- {
- int ldisc = NTTYDISC;
-
- if( flags.connected ) // do nothing if already connected
- return YES;
-
- if( ![self deviceIsAvailable:device])
- {
- status = MISC_SP_CANT_LOCK;
- return NO;
- }
-
- portFD = open( device,O_RDWR );
- if( portFD < 0 )
- return NO;
-
- // Save current parameters
- ioctl( portFD, TIOCGETD, &origLineDisc );
- ioctl( portFD, TIOCGETP, &origTtyParms );
- ioctl( portFD, TIOCGETC, &origSpecChars );
- ioctl( portFD, TIOCLGET, &origLocMode );
- ioctl( portFD, TIOCGLTC, &origLocSpecChars );
-
- ttyParms = origTtyParms;
-
- // set new discipline
- ioctl( portFD, TIOCSETD, &ldisc );
- // pass our parameters structure to the driver ( I/O buffers are flushed )
- ioctl( portFD, TIOCSETD, &ttyParms );
-
- flags.connected = YES;
-
- // setup file descriptor monitor, the function we register willl be called when something
- // pops up on the port.
- DPSAddFD( portFD, ( DPSFDProc )portEventHandler, self, NX_MODALRESPTHRESHOLD );
- flags.suspended = NO;
-
- status = MISC_SP_OK;
- return YES;
- }
-
- //========================================================================
- - disconnect
- {
- if( !flags.connected )
- return self;
-
- if( !flags.suspended )
- DPSRemoveFD( portFD );
-
- // Be a good object and put everything back as it was when we took it!
- ioctl( portFD, TIOCSETD, &origLineDisc );
- ioctl( portFD, TIOCSETP, &origTtyParms );
- ioctl( portFD, TIOCSETC, &origSpecChars );
- ioctl( portFD, TIOCLSET, &origLocMode );
- ioctl( portFD, TIOCSLTC, &origLocSpecChars );
-
- // O.K. I dont bother setting back the "exclusive-use" bit if it has
- // changed, but does it really matter? Anyway, the "hang-up-on-close" bit
- // does not seem to be reset-able !
-
- if(close( portFD )<0)
- {
- status = MISC_SP_SEE_ERRNO;
- return self;
- }
- flags.connected = NO;
- status = MISC_SP_OK;
- return self;
- }
-
- //========================================================================
- // The convention is to have the accessor and modifier methods use
- // the ivar name, however setDevice is a method declared in NXSoundStream!
- - setDeviceName: ( const char * )name
- {
- if( strcmp( name,device ) == 0 )
- return self;
-
- strncpy( device,name,DEVICE_PATH_LEN );
- device[DEVICE_PATH_LEN+1] = '\0';
-
- if( ![self deviceIsAvailable:name])
- {
- status = (MISC_SP_CANT_LOCK & MISC_SP_NO_DEVICE & MISC_SP_NOT_OPEN);
- return NO;
- }
-
- if( flags.connected )
- {
- [self disconnect];
- [self connect];
- }
- return self;
- }
-
- //========================================================================
- - ( BOOL ) deviceIsAvailable: ( const char * )name
- {
- id lockFilename;
-
- lockFilename = [[MiscString alloc] initString:"/usr/spool/uucp/LCK/LCK.."];
-
- // concat the name of the device to the lockfile name
- [lockFilename concatenate:[[lockFilename filename] stringValueAndFree]];
-
- miscLock = [[MiscLockFile alloc] init];
- [miscLock setFileName:lockFilename];
-
- if ([miscLock lock] )
- {
- [miscLock unlock];
- return YES;
- }
- else
- return NO;
- }
-
- //========================================================================
- - setFlowControl:( int )method;
- {
- switch( method )
- {
- case MISC_SP_NOFC:
- ttyParms.sg_flags &= ~TANDEM; // clear XON/XOFF Flow control bit
- flags.hardwareFC = NO;
- break;
-
- case MISC_SP_HARDFC:
- ttyParms.sg_flags &= ~TANDEM; // clear XON/XOFF Flow control bit
- flags.hardwareFC = YES;
- break;
-
- case MISC_SP_SOFTFC:
- ttyParms.sg_flags |= TANDEM; // set XON/XOFF Flow control bit
- flags.hardwareFC = NO;
- break;
-
- default:
- ttyParms.sg_flags &= ~TANDEM; // clear XON/XOFF Flow control bit
- flags.hardwareFC = YES;
- break;
- }
-
- if( flags.connected )
- ioctl( portFD,TIOCSETP,&ttyParms );
-
- status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
- return self;
- }
-
- //========================================================================
- - (int) flowControl;
- {
- if( flags.hardwareFC )
- return MISC_SP_HARDFC;
-
- if( ttyParms.sg_flags & TANDEM )
- return MISC_SP_SOFTFC;
- else
- return MISC_SP_NOFC;
- }
-
- //========================================================================
- - dropDTR
- {
- if( flags.connected )
- ioctl( portFD, TIOCCDTR,0 );
- status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
- return self;
- }
-
- //========================================================================
- - raiseDTR
- {
- if( flags.connected )
- ioctl( portFD, TIOCSDTR,0 );
- status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
- return self;
- }
-
- //========================================================================
- - setBaudRateByName: ( int )name
- {
- ttyParms.sg_ispeed = name;
- ttyParms.sg_ospeed = name;
- if( flags.connected )
- ioctl( portFD,TIOCSETP,&ttyParms );
- status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
- return self;
- }
-
- //========================================================================
- - setBaudRate: ( int )speed
- {
- UBYTE i=0;
-
- // we try to find the first standard speed at least equal to what is requested
- speed = ( int ) ceil( speed );
-
- while( baudRateTable[i].name )
- {
- if( baudRateTable[i].intValue >=speed )
- return [self setBaud: baudRateTable[i].name];
- i++;
- }
- [self setBaud: B19200];
- return self;
- }
-
- //========================================================================
- - setBaudRateFromString: ( const char* )speed
- {
- // I thought of doing an sprintf on speed to check for a numerical value but it would probably
- // be useful in 1 case over 42:-).
- return [self setBaudRate:atoi( speed )];
- }
-
- //========================================================================
- - setParity: ( int )parity
- {
- switch( parity )
- {
- case MISC_SP_NONE:
- ttyParms.sg_flags &= ( ~EVENP | ~ODDP );
- break;
-
- case MISC_SP_ODD:
- ttyParms.sg_flags |= ODDP;
- ttyParms.sg_flags &= ~EVENP;
- break;
-
- case MISC_SP_EVEN:
- ttyParms.sg_flags |= EVENP;
- ttyParms.sg_flags &= ~ODDP;
- break;
-
- default:
- ttyParms.sg_flags &= ( ~EVENP | ~ODDP );
- break;
- }
-
- ioctl( portFD,TIOCSETP,&ttyParms );
-
- return self;
- }
-
- //========================================================================
- - (int) parity;
- {
- // The EVENP bit has precedence whether or not the ODDP is set
- if( ttyParms.sg_flags & EVENP )
- return MISC_SP_EVEN;
-
- if( ttyParms.sg_flags & ODDP )
- return MISC_SP_ODD;
- else // <=> none of them are set
- return MISC_SP_NONE;
- }
-
- //========================================================================
- - setMode: ( int )newMode
- {
- switch( newMode )
- {
- case MISC_SP_COOKED:
- ttyParms.sg_flags &= ~CBREAK;
- ttyParms.sg_flags &= ~RAW;
- break;
-
- case MISC_SP_CBREAK:
- ttyParms.sg_flags |= CBREAK;
- ttyParms.sg_flags &= ~RAW;
- break;
-
- case MISC_SP_RAW:
- ttyParms.sg_flags |= RAW;
- ttyParms.sg_flags &= ~CBREAK;
- break;
-
- default:
- ttyStruct.sg_flags &= ~CBREAK;
- ttyStruct.sg_flags &= ~RAW;
- break;
- }
-
- if( flags.connected ) // this flag ensures that the file descriptor is valid
- ioctl( portFD,TIOCSETP,&ttyParms );
-
- return self;
- }
-
- //========================================================================
- - (int) mode;
- {
- // I *think * that the RAW bit has precedence over the CBREAK
- if( ttyParms.sg_flags & RAW )
- return MISC_SP_RAW;
-
- if( ttyParms.sg_flags & CBREAK )
- return MISC_SP_CBREAK;
- else // <=> none of them are set
- return MISC_SP_COOKED;
- }
-
- //========================================================================
- - translateNL: ( BOOL )yesNo
- {
- if( yesNo )
- ttyParms.sg_flags |= CRMOD;
- else
- ttyParms.sg_flags &= ~CRMOD;
-
- if( flags.connected ) // this flag ensures that the file descriptor is valid
- ioctl( portFD,TIOCSETP,&ttyParms );
-
- status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
-
- return self;
- }
-
- //========================================================================
- - setEchoing: ( BOOL )yesNo
- {
- if( yesNo )
- ttyParms.sg_flags |= ECHO;
- else
- ttyParms.sg_flags &= ~ECHO;
-
- if( flags.connected ) // this flag ensures that the file descriptor is valid
- ioctl( portFD,TIOCSETP,&ttyParms );
-
- status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
-
- return self;
- }
-
- //========================================================================
- - setDelegate: theConsumer
- {
- // we only accpet the delegate if it responds to the reception notification message
- if( [theConsumer respondsTo:@selector( receiveChars:length: )] )
- {
- delegate = theConsumer;
- status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
- return self;
- }
- delegate=nil;
- status = MISC_SP_OTHER; // this is just not to say we're OK if the delegate was refused.
-
- return nil; // the return value is the thing to check in this case!
- }
-
- //========================================================================
- - transmitChars: ( const char * )buffer length: ( int )length
- {
- if( !flags.connected )
- {
- status = MISC_SP_NOT_OPEN;
- return self;
- }
-
- if( write( portFD,buffer,length ) < 0)
- {
- status = MISC_SP_SEE_ERRNO;
- return self;
- }
-
- status = MISC_SP_OK;
- return self;
- }
-
- //========================================================================
- - ( void )checkReceiver
- {
- #define BUF_LEN 2048
-
- int red;
- char buf[BUF_LEN];
-
- red = read( portFD,buf, BUF_LEN );
-
- // If we got here something HAS arrived at the port I'm not sure if the check is useful
- // On the other hand is this comparison significantly long?
- if( red > 0 )
- [delegate receiveChars: buf length: red];
- }
-
- //========================================================================
- void *portEventHandler( int fdtag, id thePort )
- {
- [thePort checkReceiver];
- return thePort;
- }
-
- //========================================================================
- - suspendIO
- {
- if( flags.connected )
- {
- if( !flags.suspended )
- DPSRemoveFD( portFD );
- flags.suspended = YES;
- }
- status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
- return self;
- }
-
- //========================================================================
- - continue { return [self resumeIO];}
-
- //========================================================================
- - resumeIO
- {
- if( flags.connected && flags.suspended )
- {
- DPSAddFD( portFD,( DPSFDProc )portEventHandler, self, NX_MODALRESPTHRESHOLD );
- flags.suspended = NO;
- }
- status = ( flags.connected ) ? MISC_SP_OK : MISC_SP_NOT_OPEN;
- return self;
- }
-
- //========================================================================
- - clearStatus;
- {
- status = MISC_SP_OK;
- return self;
- }
-
- - ( BOOL ) isSuspended { return flags.suspended; }
- - ( int ) fileDescriptor { return portFD; }
- - ( int ) status; { return ( int ) status;}
- - ( const char* ) statusString { return "OK!"; }
- - ( BOOL ) isConnected { return flags.connected; }
- - delegate { return delegate; }
- - ( BOOL ) isEchoing { return ( ttyParms.sg_flags & ECHO ); }
- - (BOOL) doesTranslateNL { return ( ttyParms.sg_flags & CRMOD ); }
- - ( int ) baudRate { return 0; }
- - ( const char* ) deviceName { return ( const char* ) device; }
-
- @end
-
-